#ifdef __KERNEL__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/version.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/clk.h>
#include <linux/of.h>
#include "ei_type.h"
#include "ei_common.h"
#include "ei_os.h"

#else
#include <rtthread.h>
#include "irq_numbers.h"
#include "ei_os.h"
#include "ei_common.h"
#endif

#include "ei_comm_vc.h"

#define JVC_BASE_ADDR            0x01300000
#define JVC_BASE_SIZE            0x100000
#define JVC_FREQUENCE            300000000

#define JVC_PARENT_CLK_RATE     600000000
#define JVC_DEF_CLK_RATE        300000000

#define JVC_AHB_GATE			"jvc_s_ahb_gate"
#define JVC_AHB_RESET			"jvc_s_ahb_reset"
#define JVC_AXI_GATE			"jvc_s_axi_gate"
#define JVC_CLK					"jvc_s_module_clk"

struct clk *jvc_clk;
struct clk *jvc_ahb_gate;
struct clk *jvc_ahb_reset;
struct clk *jvc_axi_gate;

int jvc_task_tgid_vnr(void);
extern int VENC_JVC_ModInit(void);
extern void JVC_DRV_HandleIsr(void);
extern int VENC_JVC_ModExit(void);

void jvc_pm_runtime_get_sync(void);
void jvc_pm_runtime_put_sync(void);

JVC_RESOURCE_S global_jvc_res;

int jvc_task_tgid_vnr(void)
{
#ifdef __KERNEL__
    return current->tgid;
#else
    return rt_thread_self();
#endif
}

int jvc_irq_handler(int irq, void *priv)
{
    JVC_DRV_HandleIsr();

    return OSAL_IRQ_HANDLED;
}

#ifdef __KERNEL__

void jvc_pm_runtime_get_sync()
{
    #ifdef CONFIG_PM_GENERIC_DOMAINS
    pm_runtime_get_sync(&(((struct platform_device *)(global_jvc_res.pDev))->dev));
    #endif
    return;
}

void jvc_pm_runtime_put_sync()
{
    #ifdef CONFIG_PM_GENERIC_DOMAINS
    pm_runtime_put_sync(&(((struct platform_device *)(global_jvc_res.pDev))->dev));
    #endif
    return;
}

#ifdef CONFIG_PM_GENERIC_DOMAINS
static int lombo_jvc_resume(struct device *dev)
{
    clk_disable_unprepare(jvc_ahb_reset);
    clk_prepare_enable(jvc_ahb_reset);
    // os_printk("jvc: debug lombo_jvc_resume %d", __LINE__);
    return 0;
}

static int ei_jvc_probe_sub(struct platform_device *pdev)
{
    pm_runtime_set_active(&pdev->dev);
    pm_runtime_enable(&pdev->dev);
    // os_printk("jvc: debug ei_jvc_probe_sub %d", __LINE__);

    return 0;
}

static int ei_jvc_remove_sub(struct platform_device *pdev)
{
    pm_runtime_disable(&pdev->dev);
    // os_printk("jvc: debug ei_jvc_remove_sub %d", __LINE__);

    return 0;
}

static const struct dev_pm_ops pm_jvc_ops = {
#ifdef CONFIG_PM_SLEEP
    SET_SYSTEM_SLEEP_PM_OPS(NULL,
        lombo_jvc_resume)
#endif
};

static const struct of_device_id ei_jvc_math[] = {
    { .compatible = "ei, ei-jvc", },
    {}
};
MODULE_DEVICE_TABLE(of, ei_jvc_math);

static struct platform_driver jvc_pm_driver = {
    .driver = {
        .name = "ei-jvc",
        .owner = THIS_MODULE,
        .of_match_table = ei_jvc_math,
        .pm = &pm_jvc_ops,
    },
    .probe = ei_jvc_probe_sub,
    .remove = ei_jvc_remove_sub,
};
#endif

int ei_jvc_probe(struct platform_device *pdev)
{
    struct resource *iores = EI_NULL;
    int iosize;
    //struct resource *res_irq = EI_NULL;
    int ret = -1;
    EI_U32 frequency;

    global_jvc_res.pDev = pdev;

#ifdef CONFIG_PM_GENERIC_DOMAINS
    platform_driver_register(&jvc_pm_driver);
#endif

    if (pdev) {
        iores = os_platform_get_resource(pdev, IORESOURCE_MEM, 0);
        global_jvc_res.pBase = iores;
    }

    if (iores == NULL) {
        os_printk("platform get resource error\n");
        return EI_FAILURE;
    }

    global_jvc_res.u32Irq = os_platform_get_irq(pdev, 0);;
    if (global_jvc_res.u32Irq < 0) {
        return EI_FAILURE;
    }

    EI_TRACE_VENC(EI_DBG_ERR, "jvc irq:%d\n", global_jvc_res.u32Irq);
    ret = os_request_irq(global_jvc_res.u32Irq, (os_irq_handler_t)jvc_irq_handler, 0, "jvc_isr", &global_jvc_res);
    if (ret) {
        os_printk("jvc : register jvc irq failed!\n");
        return EI_FAILURE;
    }

    iosize = iores->end - iores->start + 1;
    global_jvc_res.pBase = os_ioremap_nocache(iores->start, iosize);
    if (IS_ERR((void *)global_jvc_res.pBase)) {
        ret = PTR_ERR((void *)global_jvc_res.pBase);
        return ret;
    }

    ret = of_property_read_u32((&pdev->dev)->of_node, "clock-frequency", &frequency);
    if (ret == -EINVAL) {
        os_printk("no have clock-rates property %d\n",__LINE__);
        frequency = JVC_DEF_CLK_RATE;
    } else if(ret == -ENODATA) {
        os_printk("no have clock-rates data %d\n",__LINE__);
        frequency = JVC_DEF_CLK_RATE;
    } else {
        EI_TRACE_VENC(EI_DBG_ERR, "jvc set rate : %d\n", frequency);
    }

    jvc_ahb_gate = devm_clk_get(&pdev->dev, JVC_AHB_GATE);
    if (IS_ERR(jvc_ahb_gate)) {
        os_printk("get jvc_ahb_gate err\n");
    }
    jvc_ahb_reset = devm_clk_get(&pdev->dev, JVC_AHB_RESET);
    if (IS_ERR(jvc_ahb_reset)) {
        os_printk("get jvc_ahb_reset err\n");
    }
    jvc_axi_gate = devm_clk_get(&pdev->dev, JVC_AXI_GATE);
    if (IS_ERR(jvc_axi_gate)) {
        os_printk("get jvc_axi_gate err\n");
    }
    jvc_clk = devm_clk_get(&pdev->dev, JVC_CLK);
    if (IS_ERR(jvc_clk)) {
        os_printk("get jvc_clk err\n");
    }

    global_jvc_res.u32ClkRate = frequency;
    ret = clk_set_rate(jvc_clk, frequency);
    if (ret != 0)
    {
        os_printk("jvc: clk prepare enable error %d\n",__LINE__);
    }

    ret = clk_prepare_enable(jvc_ahb_reset);
    if (ret != 0) {
        os_printk("jvc: reset clk enable error %d\n",__LINE__);
        return EI_FAILURE;
    }

#ifdef CONFIG_PM_GENERIC_DOMAINS
	ret = pm_clk_add_clk(&pdev->dev, jvc_clk);
	ret |= pm_clk_add_clk(&pdev->dev, jvc_ahb_gate);
	ret |= pm_clk_add_clk(&pdev->dev, jvc_axi_gate);
#else
	ret = clk_prepare_enable(jvc_ahb_gate);
	ret |= clk_prepare_enable(jvc_axi_gate);
	ret |= clk_prepare_enable(jvc_clk);
#endif

    if (ret != 0)
    {
        os_printk("jvc: clk prepare enable error %d\n",__LINE__);
        return EI_FAILURE;
    }

    os_printk("jvc: clk prepare enable ok\n");

    ret = VENC_JVC_ModInit();
    if (ret < 0) {
        os_printk("jvc mod init error\n");
        return EI_FAILURE;
    }


    return EI_SUCCESS;
}

int ei_jvc_remove(struct platform_device *pdev)
{

    if (global_jvc_res.u32Irq != 0) {
        os_free_irq(global_jvc_res.u32Irq, pdev);
    }

#ifdef CONFIG_PM_GENERIC_DOMAINS
	pm_runtime_put_sync(&pdev->dev);
	pm_runtime_disable(&pdev->dev);
	pm_clk_remove_clk(&pdev->dev, jvc_ahb_gate);
	pm_clk_remove_clk(&pdev->dev, jvc_axi_gate);
	pm_clk_remove_clk(&pdev->dev, jvc_clk);
#else
	clk_disable_unprepare(jvc_ahb_gate);
	clk_disable_unprepare(jvc_axi_gate);
	clk_disable_unprepare(jvc_clk);
#endif
	clk_disable_unprepare(jvc_ahb_reset);

    VENC_JVC_ModExit();
    os_printk("%s\n", "jvc exit ok");

    return 0;
}

#else

int jvc_prcm_clk_powerup(void)
{
    unsigned int pram_base_addr = 0x0400a000;
    //unsigned int ret = 0;
    unsigned int regval;
    unsigned int iosize = 0x0a00;
    unsigned char *prcm_vir_addr;
    unsigned int *wrt;
    unsigned int *rd;


    prcm_vir_addr = os_ioremap_nocache(pram_base_addr, iosize);

    rd = (unsigned int *)(prcm_vir_addr + 0x0340);
    regval = *rd;

	regval = regval | (1 << 1);
    wrt = (unsigned int *)(prcm_vir_addr + 0x0340);
    *wrt = regval;


	//AHB BUS CLOCK GATING
    rd = (unsigned int *)(prcm_vir_addr + 0x0204);
    regval = *rd;

	regval = regval | (1 << 12);
    wrt = (unsigned int *)(prcm_vir_addr + 0x0204);
    *wrt = regval;

	//AHB BUS Reset/VC Reset
    rd = (unsigned int *)(prcm_vir_addr + 0x0224);
    regval = *rd;

	regval = regval & 0xffffefff;

    wrt = (unsigned int *)(prcm_vir_addr + 0x0224);
    *wrt = regval;

	os_msleep(5);
	regval = regval | (1 << 12);

    wrt = (unsigned int *)(prcm_vir_addr + 0x0224);
    *wrt = regval;

    wrt = (unsigned int *)(prcm_vir_addr + 0x09c0);
    *wrt = 0x80000011;
	os_msleep(5);

    return 0;
}

int jvc_driver_init()
{
    unsigned long res_phy_addr;
    unsigned long res_vir_addr;
    unsigned int frequency;
    unsigned int facfre;

    int res_size;
    int irq = 0;
    int ret = 0;
    unsigned int regval;
    int retry_cnt = 10, times_retry = 0;

    os_printk("jvc_driver_init start \n");
    // global_jvc_res.pDev = pdev;

    res_phy_addr = JVC_BASE_ADDR;
    res_size = JVC_BASE_SIZE;
    frequency = JVC_FREQUENCE;

    os_printk("jvc frequency:%d\n", frequency);

    global_jvc_res.pBase = (unsigned long)os_ioremap_nocache(res_phy_addr, res_size);

    global_jvc_res.u32Irq = INT_JPEG;
    if (global_jvc_res.u32Irq < 0) {
        return EI_FAILURE;
    }
    os_printk("jvc irq:%d\n", global_jvc_res.u32Irq);
    ret = os_request_irq(global_jvc_res.u32Irq, (os_irq_handler_t)jvc_irq_handler, 0, "jvc_isr", &global_jvc_res);
    if (ret) {
        os_printk("jvc : register jvc irq failed!\n");
        return EI_FAILURE;
    }

    jvc_prcm_clk_powerup();

    global_jvc_res.u32ClkRate = frequency;

    ret = VENC_JVC_ModInit();
    if (ret < 0) {
        os_printk("jvc mod init error\n");
        return EI_FAILURE;
    }

    return EI_SUCCESS;
}
#endif
